package com.dekaru.math.dm;

import java.io.*;
import java.util.StringTokenizer;
import java.util.Vector;

import com.dekaru.data.Stack;
import com.dekaru.math.type.Matrix;
import com.dekaru.util.*;

public class DMcmd {

	private DMparser parser;
	private DMalu alu;
	private PrintReader io;
	
	public DMcmd() {
		this.parser = new DMparser();
		this.alu = new DMalu();
		this.io = new PrintReader();
	}
	
	private static final String ERROR = "ERROR: unknown input syntax \n";
	private static final String ERROR_DATA = "ERROR: invalid data format \n";
	private static final String ERROR_EXEC = "ERROR: invalid instruction \n";
	private static final String ERROR_FILE = "ERROR: file not found \n";
	/*
matrix a
1.5 2.3
5.0 10

matrix b [[1,2][3,4]]

sum a b
	 */
	public String openFile(String file) {
		io.setFileIn(file);
		String result = new String();
		result += "-- Archivo abierto correctamente.\n";
		
		try {
			while (io.ready()) {
				result += this.executeCommand(io.readLinef()) + "\n";
			}
		} catch (IOException ioe) {
			return ERROR_EXEC;
		}
		
		return result;
	}
	
	public String executeCommand(String stm) {
		
		StringTokenizer stk = new StringTokenizer(stm, " ");
		String cmd = stk.nextToken();
		
		int opt = parser.indexOf(cmd.toLowerCase());
		switch (opt) {
			case 1 : return matrix(stk);
			case 2 : return sum(stk);
			default: return ERROR_EXEC;
		}
	}
	
	public String matrix(StringTokenizer stk) {
		
		String result = new String();
		
		if (stk.countTokens() == 1) {
			if (io.isFileInEnabled()) {
				String name = stk.nextToken();
				Object m = readMatrixFromFile();
				if (m instanceof Matrix) {
					Matrix matrix = (Matrix) m;
					alu.put(name, matrix);
					return name + " = \n" + matrix.toString();
				} else {
					return ERROR_DATA;
				}
			} else {
				return ERROR_FILE;
			}
		} else {
			if (stk.countTokens() == 2) {
				String name = stk.nextToken();
				Object m = readMatrixFromCmd(stk.nextToken());
				if (m instanceof Matrix) {
					Matrix matrix = (Matrix) m;
					alu.put(name, matrix);
					return name + " = \n" + matrix.toString();
				} else {
					return ERROR_DATA;
				}
			} else {
				return ERROR_EXEC;
			}
		}
	}
	
	public String sum(StringTokenizer stk) {
		String n1 = stk.nextToken();
		String n2 = stk.nextToken();
		return n1 + " + " + n2 + " = \n" + alu.sum(n1, n2);
	}
	
	public Object readMatrixFromCmd(String s) throws NumberFormatException, NullPointerException {
		
		Vector<Vector> rows = new Vector<Vector>();
		Vector<Double> cols = new Vector<Double>();
		Stack stack = new Stack();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (stack.size() == 0 && c == '[') {
				stack.push('%');
			} else {
				if (stack.size() == 1 && c == '[') {
					stack.push('%');
					cols = new Vector<Double>();
				} else {
					if (stack.size() == 2) {
						if (c != ']' && c != ',') {
							char ch;
							if (s.indexOf(',', i+1) < s.indexOf(']', i+1) && s.indexOf(',', i+1) != -1) {
								ch = ',';
							} else {
								ch = ']';
							}
							int skip = s.indexOf(ch, i) - i -1;
							String sd = s.substring(i, s.indexOf(ch, i));
							i += skip;
							cols.add(Double.parseDouble(sd));
						} else {
							if (c == ']') {
								stack.pop();
								rows.add(cols);
							}
						}
					} else {
						if (stack.size() == 1 && c == ']') {
							stack.pop();
							return new Matrix(rows);
						} else {
							throw new NullPointerException();
						}
					}
				}
			}
		}
		return ERROR;
	}

	public Object readMatrixFromFile() {
		
		Vector<Vector> rows = new Vector<Vector>();
		try {
			String s = io.readLinef();
			while (!s.equals(".")) {
				Vector<Double> cols = new Vector<Double>();
    			StringTokenizer stk = new StringTokenizer(s, " ");
    			while (stk.hasMoreTokens()) {
					cols.add(Double.parseDouble((stk.nextToken())));
    			}
    			rows.add(cols);
    			s = io.readLinef();
			}
			return new Matrix(rows);
		} catch(NumberFormatException nfe) {
			return nfe.toString();
		} catch(IOException ioe) {
			return ioe.toString();
		} catch(NullPointerException npe) {
			return new Matrix(rows);
		}
	}
}
